/**
 * @class wosh::UserInfoLocation   
 * @brief  
 *
 ****************************************************************************
 * @version $Id: UserInfoLocation.h 2863 2010-08-07 04:28:58Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR UserS;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_UserInfoLocation_H__
 #define __WOSH_Core_UserInfoLocation_H__

 #include <woshDefs.h>
 #include <core/Containers.h>
 #include <core/Serialization.h>


namespace wosh {

struct UserInfoLocationData {
	long timestamp;			///< epoch
	double probability;		///< [0,1]
};

class UserInfoLocation : public virtual ISerializable
 {
	WOSH_SERIALIZABLE(wosh::UserInfoLocation)
	WOSH_COMPARE_RTTI(wosh::UserInfoLocation)

	typedef MapT<std::string, UserInfoLocationData> tStrUserInfoLocationDataMap;


	public:

		UserInfoLocation() { }

		UserInfoLocation( const UserInfoLocation& m ) {
			*this = m;
		 }

		virtual ~UserInfoLocation() { }


//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:

		void update( const std::string& location, UserInfoLocationData info ) {
			this->locations.transactionBeginWrite();
			this->locations.set(location, info);
			this->locations.transactionEnd();
		 }

		void update( const std::string& location, long timestamp, double probability ) {
			this->locations.transactionBeginWrite();
			UserInfoLocationData info;
			info.timestamp = timestamp;
			info.probability = probability;
			this->locations.set(location, info);
			this->locations.transactionEnd();
		 }

		///< updates only probability when exists, else set probability to 0
		void updateTimeStampOf( const std::string& location, long timestamp ) {
			this->locations.transactionBeginWrite();
			UserInfoLocationData info;
			info.probability = 0;

			if ( this->locations.exists(location) )
				info = this->locations.find(location);
			info.timestamp = timestamp;
			this->locations.set(location, info);
			this->locations.transactionEnd();
		 }

		///< updates only probability when exists, else set timestamp to 0
		void updateProbabilityOf( const std::string& location, double probability, bool offset = false ) {
			this->locations.transactionBeginWrite();
			UserInfoLocationData info;
			info.timestamp = 0;
			info.probability = 0;
			if ( this->locations.exists(location) )
				info = this->locations.find(location);
			if ( offset ) {
				info.probability += probability;
				if ( info.probability < 0 ) info.probability = 0;
				//if ( info.probability > 1.0 ) info.probability = 1.0;
			 }
			else
				info.probability = probability;
			this->locations.set(location, info);
			this->locations.transactionEnd();
		 }

		void filterTimedOut( long epoch ) {
			this->locations.transactionBeginWrite();
			tStrUserInfoLocationDataMap::Iterator it;
			const tStrUserInfoLocationDataMap::ConstIterator it_end = this->locations.end();
			for ( it=this->locations.begin(); it!=it_end; ++it ) {
				if ( it->second.timestamp >= epoch ) continue;
				this->locations.erase(it);
			 }
			this->locations.transactionEnd();
		 }


//@}
/** @name Getters
 * @{
 ******************************************************************************/
	public:

		tStrUserInfoLocationDataMap& getLocations()				{ return this->locations; }
		const tStrUserInfoLocationDataMap& getLocations() const	{ return this->locations; }

		void getLocationLast( std::string& location, UserInfoLocationData& info ) const {
			location = "";
			info.timestamp = LIMIT_LONG_MIN;
			info.probability = 0;
			this->locations.transactionBeginRead();
			tStrUserInfoLocationDataMap::ConstIterator it;
			const tStrUserInfoLocationDataMap::ConstIterator it_end = this->locations.end();
			for ( it=this->locations.begin(); it!=it_end; ++it ) {
				if ( it->second.timestamp < info.timestamp ) continue;
				location = it->first;
				info = it->second;
			 }
			this->locations.transactionEnd();
		 }

		std::string getLastName() const {
			std::string location = ""; UserInfoLocationData commData;
			getLocationLast( location, commData );
			return location;
		 }

		UserInfoLocationData getLastData() const {
			std::string location = ""; UserInfoLocationData commData;
			getLocationLast( location, commData );
			return commData;
		 }

		void getLocationRank( std::string& location, UserInfoLocationData& info ) const {
			location = "";
			info.probability = -1;
			info.timestamp = LIMIT_LONG_MIN;
			this->locations.transactionBeginRead();
			tStrUserInfoLocationDataMap::ConstIterator it;
			const tStrUserInfoLocationDataMap::ConstIterator it_end = this->locations.end();
			for ( it=this->locations.begin(); it!=it_end; ++it ) {
				if ( it->second.probability < info.probability ) continue;
				if ( it->second.probability == info.probability && it->second.timestamp < info.timestamp ) continue;
				location = it->first;
				info = it->second;
			 }
			this->locations.transactionEnd();
		 }

//@}
/** @name Operators Overloading
 * @{
 ******************************************************************************/
	public:
		virtual UserInfoLocation& operator=(const UserInfoLocation& m) {
			m.locations.transactionBeginRead();
			this->locations.transactionBeginWrite();
			tStrUserInfoLocationDataMap::ConstIterator it;
			const tStrUserInfoLocationDataMap::ConstIterator it_end = m.locations.end();
			for ( it=m.locations.begin(); it!=it_end; ++it ) {
				this->locations.set( it->first, it->second );
			 }
			this->locations.transactionEnd();
			m.locations.transactionEnd();
			return *this;
		 }

		virtual bool operator==(const UserInfoLocation &other) const {
			if ( this->locations.count() != other.locations.count() )
				return false;
			return false;
		 }

		virtual bool operator!=(const UserInfoLocation &other) const	{ return !(*this == other);  }

//@}
/** @name Serialization interface
 * @{
 ******************************************************************************/
	public:
		virtual std::string toString() const			{ return ""; }
		virtual bool updateHash( DataHashContext* hash_context ) const {
			if ( hash_context == NULL ) return false;
			bool ret = true;
			tStrUserInfoLocationDataMap::ConstIterator it;
			const tStrUserInfoLocationDataMap::ConstIterator it_end = this->locations.end();
			for ( it=this->locations.begin(); it!=it_end; ++it ) {
				ret = hash_context->update( it->first ) & ret;
				ret = hash_context->update( it->second.timestamp ) & ret;
				ret = hash_context->update( it->second.probability ) & ret;
			 }
			return ret;
		 }
//@}
	protected:
		tStrUserInfoLocationDataMap locations;	///< commmunicators


}; // class def

}; // namespace wosh

#endif //__WOSH_Core_UserInfoLocation_H__
